
#ifndef PARTICLE_EMITTER_H
#define PARTICLE_EMITTER_H
#include <math.h>
#include <set>
#include <Box2D/Box2D.h>


// Callback used to notify the user of created particles.
class EmittedParticleCallback {
public:
	EmittedParticleCallback() {}
	virtual ~EmittedParticleCallback() {}

	// Called for each created particle.
	virtual void ParticleCreated(b2ParticleSystem * const system,
								 const int32 particleIndex) = 0;
};


// Assigns a random lifetime to each created particle.
class ParticleLifetimeRandomizer : public EmittedParticleCallback
{
public:
	// Initialize the randomizer to set lifetimes between minLifetime to
	// maxLifetime.
	ParticleLifetimeRandomizer(const float32 minLifetime,const float32 maxLifetime) :
		mMinLifetime(minLifetime), mMaxLifetime(maxLifetime) { }
	virtual ~ParticleLifetimeRandomizer() { }
	// Called for each created particle.
	virtual void ParticleCreated(b2ParticleSystem * const system,const int32 particleIndex) {
		system->SetParticleLifetime(particleIndex, ((float32)rand() / (float32)RAND_MAX) *
									(mMaxLifetime - mMinLifetime) + mMinLifetime);
	}

private:
	float32 mMinLifetime;
	float32 mMaxLifetime;
};


// Emit particles from a circular region.
class RadialEmitter {
public:
	// Initialize a particle emitter.
	RadialEmitter() :
		mParticleSystem(NULL), mCallback(NULL), mSpeed(0.0f),
		mEmitRate(1.0f), mEmitRemainder(0.0f), mFlags(b2_waterParticle),
		mGroup(NULL)
	{
	}

	~RadialEmitter()
	{
		SetGroup(NULL);
	}

	// Set the center of the emitter.
	void SetPosition(const b2Vec2& origin)
	{
		mOrigin = origin;
	}

	// Get the center of the emitter.
	const b2Vec2& GetPosition() const
	{
		return mOrigin;
	}

	// Set the size of the circle which emits particles.
	void SetSize(const b2Vec2& size)
	{
		mHalfSize = size * 0.5f;
	}

	// Get the size of the circle which emits particles.
	b2Vec2 GetSize() const
	{
		return mHalfSize * 2.0f;
	}

	// Set the starting velocity of emitted particles.
	void SetVelocity(const b2Vec2& velocity)
	{
		mStartingVelocity = velocity;
	}

	// Get the starting velocity.
	const b2Vec2& GetVelocity() const
	{
		return mStartingVelocity;
	}

	// Set the speed of particles along the direction from the center of
	// the emitter.
	void SetSpeed(const float32 speed)
	{
		mSpeed = speed;
	}

	// Get the speed of particles along the direction from the center of
	// the emitter.
	float32 GetSpeed() const
	{
		return mSpeed;
	}

	// Set the flags for created particles.
	void SetParticleFlags(uint32 flags)
	{
		mFlags = flags;
	}

	// Get the flags for created particles.
	uint32 GetParticleFlags() const
	{
		return mFlags;
	}

	// Set the color of particles.
	void SetColor(const b2ParticleColor& color)
	{
		mColor = color;
	}

	// Get the color of particles emitter.
	const b2ParticleColor& GetColor() const
	{
		return mColor;
	}

	// Set the emit rate in particles per second.
	void SetEmitRate(const float32 emitRate)
	{
		mEmitRate = emitRate;
	}

	// Get the current emit rate.
	float32 GetEmitRate() const
	{
		return mEmitRate;
	}

	// Set the particle system this emitter is adding particles to.
	void SetParticleSystem(b2ParticleSystem * const particleSystem)
	{
		mParticleSystem = particleSystem;
	}

	// Get the particle system this emitter is adding particle to.
	b2ParticleSystem* GetParticleSystem() const
	{
		return mParticleSystem;
	}

	// Set the callback that is called on the creation of each particle.
	void SetCallback(EmittedParticleCallback* const callback)
	{
		mCallback = callback;
	}

	// Get the callback that is called on the creation of each particle.
	EmittedParticleCallback* GetCallback() const
	{
		return mCallback;
	}

	// This class sets the group flags to b2_particleGroupCanBeEmpty so that
	// it isn't destroyed and clears the b2_particleGroupCanBeEmpty on the
	// group when the emitter no longer references it so that the group
	// can potentially be cleaned up.
	void SetGroup(b2ParticleGroup * const group)
	{
		if (mGroup)
		{
			mGroup->SetGroupFlags(mGroup->GetGroupFlags() &
								   ~b2_particleGroupCanBeEmpty);
		}
		mGroup = group;
		if (mGroup)
		{
			mGroup->SetGroupFlags(mGroup->GetGroupFlags() |
								   b2_particleGroupCanBeEmpty);
		}
	}

	// Get the group particles should be created within.
	b2ParticleGroup* GetGroup() const
	{
		return mGroup;
	}

	// Speed up 
	void speedUp(float _deltaSpeed) {
		mStartingVelocity.x += _deltaSpeed;
		log("ASpeedUp %f", mStartingVelocity.x);
	}

	// dt is seconds that have passed, particleIndices is an optional pointer
	// to an array which tracks which particles have been created and
	// particleIndicesCount is the size of the particleIndices array.
	// This function returns the number of particles created during this
	// simulation step.
	int32 Step(const float32 dt, int32* const particleIndices,
			   const int32 particleIndicesCount)
	{
		b2Assert(mParticleSystem);
		int32 numberOfParticlesCreated = 0;
		// How many (fractional) particles should we have emitted this frame?
		mEmitRemainder += mEmitRate * dt;

		b2ParticleDef pd;
		pd.color = mColor;
		pd.flags = mFlags;
		pd.group = mGroup;
		// Keep emitting particles on this frame until we only have a
		// fractional particle left.
		while (mEmitRemainder > 1.0f) {
			mEmitRemainder -= 1.0f;

			// Randomly pick a position within the emitter's radius.
			const float32 angle = 2.0f * b2_pi;
			// Distance from the center of the circle.
			const float32 distance = 0.0f;
			b2Vec2 positionOnUnitCircle(sin(angle), cos(angle));

			// Initial position.
			pd.position.Set(
				mOrigin.x + positionOnUnitCircle.x * distance * mHalfSize.x,
				mOrigin.y + positionOnUnitCircle.y * distance * mHalfSize.y);
			// Send it flying
			pd.velocity = mStartingVelocity;
			log("SpeedUp %f", mStartingVelocity.x);
			if (mSpeed != 0.0f)
			{
				pd.velocity += positionOnUnitCircle * mSpeed;
			}

			const int32 particleIndex = mParticleSystem->CreateParticle(pd);
			if (mCallback)
			{
				mCallback->ParticleCreated(mParticleSystem, particleIndex);
			}
			if (particleIndices &&
				numberOfParticlesCreated < particleIndicesCount)
			{
				particleIndices[numberOfParticlesCreated] = particleIndex;
			}
			++numberOfParticlesCreated;
		}
		return numberOfParticlesCreated;
	}

private:
	// Calculate a random number 0.0f..1.0f.
	static float32 Random()
	{
		return ((float32)rand() / (float32)RAND_MAX);
	}

private:
	// Pointer to global world
	b2ParticleSystem *			mParticleSystem;
	// Called for each created particle.
	EmittedParticleCallback *	mCallback;
	// Center of particle emitter
	b2Vec2 						mOrigin;
	// Launch direction.
	b2Vec2 						mStartingVelocity;
	// Speed particles are emitted
	float32 					mSpeed;
	// Half width / height of particle emitter
	b2Vec2 						mHalfSize;
	// Particles per second
	float32 					mEmitRate;
	// Initial color of particle emitted.
	b2ParticleColor 			mColor;
	// Number particles to emit on the next frame
	float32 					mEmitRemainder;
	// Flags for created particles, see b2ParticleFlag.
	uint32 						mFlags;
	// Group to put newly created particles in.
	b2ParticleGroup* 			mGroup;
};

#endif  // PARTICLE_EMITTER_H


